package com.beck.code.generator.plugin;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.resource.NoResourceException;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import com.beck.code.generator.ParserEntityResolver;
import com.beck.code.generator.configProperty.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @author CuiJianbo
 * @since 2025/7/1
 */
public class ConfigurationParser {

    public static ConfigurationParser getInstance() {
        return new ConfigurationParser();
    }

    public Configuration parseXmlConfigFile() throws ParserConfigurationException, IOException, SAXException {
        InputStream inputStream = ResourceUtil.getStream("generatorConfig.xml");
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        factory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        builder.setEntityResolver(new ParserEntityResolver());
        Document document = builder.parse(inputStream);
        Element rootNode = document.getDocumentElement();
        if (rootNode.getNodeType() == 1 && document.getDoctype().getPublicId().equals("-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN")) {
            return this.parseConfiguration(rootNode);
        }
        return null;
    }

    public Configuration parseConfiguration(Element rootNode) {
        Configuration configuration = new Configuration();
        NodeList nodeList = rootNode.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (node.getNodeType() == 1) {
                this.parseContext(configuration, node);
            }
        }
        return configuration;
    }

    private void parseContext(Configuration configuration, Node node) {
        ContextConfig context = new ContextConfig();
        Properties attributes = this.parseAttributes(node);
        context.setPrimaryKeyStrategy(attributes.getProperty("primaryKeyStrategy"));
        context.setTablePrefix(attributes.getProperty("tablePrefix"));
        context.setTemplatePath(attributes.getProperty("templatePath"));
        context.setTargetModule(attributes.getProperty("targetModule"));
        context.setAuthor(attributes.getProperty("author"));
        configuration.setContext(context);
        NodeList nodeList = node.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); ++i) {
            Node childNode = nodeList.item(i);
            if (childNode.getNodeType() == 1) {
                if ("jdbcConnection".equals(childNode.getNodeName())) {
                    this.parseParseJdbcConnection(context, childNode);
                } else if ("javaTypeResolver".equals(childNode.getNodeName())) {
                    this.parseParseJavaTypeResolver(context, childNode);
                } else if ("controllerGenerator".equals(childNode.getNodeName())) {
                    this.parseControllerGenerator(context, childNode);
                } else if ("serviceGenerator".equals(childNode.getNodeName())) {
                    this.parseServiceGenerator(context, childNode);
                } else if ("entityGenerator".equals(childNode.getNodeName())) {
                    this.parseEntityGenerator(context, childNode);
                } else if ("daoGenerator".equals(childNode.getNodeName())) {
                    this.parseDaoGenerator(context, childNode);
                } else if ("mapperXmlGenerator".equals(childNode.getNodeName())) {
                    this.parseMapperXmlGenerator(context, childNode);
                } else if ("dtoGenerator".equals(childNode.getNodeName())) {
                    this.parseDtoGenerator(context, childNode);
                } else if ("voGenerator".equals(childNode.getNodeName())) {
                    this.parseVoGenerator(context, childNode);
                } else if ("table".equals(childNode.getNodeName())) {
                    this.parseTable(context, childNode);
                } else if ("extraProperties".equals(childNode.getNodeName())) {
                    this.parseExtraPropertyGenerator(context, childNode);
                }
            }
        }

    }


    private void parsePrimaryKeyStrategy(ContextConfig context, Node node) {
        PrimaryKeyStrategyConfig primaryKeyStrategyConfig = new PrimaryKeyStrategyConfig();
        Properties attributes = this.parseAttributes(node);
        String targetPackage = attributes.getProperty("strategy");
        primaryKeyStrategyConfig.setStrategy(targetPackage);
        context.setPrimaryKeyStrategyConfig(primaryKeyStrategyConfig);
    }

    private void parseParseJdbcConnection(ContextConfig context, Node node) {
        JdbcConnectionConfig jdbcConnectionConfig = new JdbcConnectionConfig();
        Properties attributes = this.parseAttributes(node);
        String connectionUrl = attributes.getProperty("connectionURL");
        String driverClass = attributes.getProperty("driverClass");
        String username = attributes.getProperty("username");
        String password = attributes.getProperty("password");
        jdbcConnectionConfig.setConnectionURL(connectionUrl);
        jdbcConnectionConfig.setDriverClass(driverClass);
        jdbcConnectionConfig.setUsername(username);
        jdbcConnectionConfig.setPassword(password);
        context.setJdbcConnectionConfig(jdbcConnectionConfig);
    }

    private void parseParseJavaTypeResolver(ContextConfig context, Node node) {
        JavaTypeResolverConfig javaTypeResolverConfig = new JavaTypeResolverConfig();
        NodeList nodeList = node.getChildNodes();
        List<DataTypeNameValue> propertyNameValues = new ArrayList<DataTypeNameValue>();
        for (int i = 0; i < nodeList.getLength(); i++) {
            DataTypeNameValue propertyNameValue = new DataTypeNameValue();
            Properties properties = this.parseAttributes(nodeList.item(i));
            propertyNameValue.setJdbcType(properties.getProperty("jdbcType"));
            propertyNameValue.setJavaType(properties.getProperty("javaType"));
            propertyNameValues.add(propertyNameValue);
        }
        javaTypeResolverConfig.setDataTypeNameValues(propertyNameValues);
        context.setJavaTypeResolverConfig(javaTypeResolverConfig);
    }

    private void parseControllerGenerator(ContextConfig context, Node node) {
        ControllerGeneratorConfig controllerGeneratorConfig = new ControllerGeneratorConfig();
        Properties attributes = this.parseAttributes(node);
        String targetModule = attributes.getProperty("targetModule");
        String targetPackage = attributes.getProperty("targetPackage");
        String template = attributes.getProperty("template");
        String overwrite = attributes.getProperty("overwrite");
        controllerGeneratorConfig.setTargetModule(targetModule);
        controllerGeneratorConfig.setTargetPackage(targetPackage);
        controllerGeneratorConfig.setTemplate(template);
        controllerGeneratorConfig.setOverwrite(StrUtil.isEmpty(overwrite) ? false : Boolean.parseBoolean(overwrite));
        context.setControllerGeneratorConfig(controllerGeneratorConfig);
    }

    private void parseServiceGenerator(ContextConfig context, Node node) {
        ServiceGeneratorConfig serviceGeneratorConfig = new ServiceGeneratorConfig();
        Properties attributes = this.parseAttributes(node);
        String targetModule = attributes.getProperty("targetModule");
        String targetPackage = attributes.getProperty("targetPackage");
        String template = attributes.getProperty("template");
        String overwrite = attributes.getProperty("overwrite");
        serviceGeneratorConfig.setTargetModule(targetModule);
        serviceGeneratorConfig.setTargetPackage(targetPackage);
        serviceGeneratorConfig.setTemplate(template);
        serviceGeneratorConfig.setOverwrite(StrUtil.isEmpty(overwrite) ? false : Boolean.parseBoolean(overwrite));
        context.setServiceGeneratorConfig(serviceGeneratorConfig);
    }

    private void parseEntityGenerator(ContextConfig context, Node node) {
        EntityGeneratorConfig entityGeneratorConfig = new EntityGeneratorConfig();
        Properties attributes = this.parseAttributes(node);
        String targetModule = attributes.getProperty("targetModule");
        String targetPackage = attributes.getProperty("targetPackage");
        String template = attributes.getProperty("template");
        String overwrite = attributes.getProperty("overwrite");
        entityGeneratorConfig.setTargetModule(targetModule);
        entityGeneratorConfig.setTargetPackage(targetPackage);
        entityGeneratorConfig.setTemplate(template);
        entityGeneratorConfig.setOverwrite(StrUtil.isEmpty(overwrite) ? false : Boolean.parseBoolean(overwrite));
        context.setEntityGeneratorConfig(entityGeneratorConfig);
    }

    private void parseDaoGenerator(ContextConfig context, Node node) {
        DaoGeneratorConfig daoGeneratorConfig = new DaoGeneratorConfig();
        Properties attributes = this.parseAttributes(node);
        String targetModule = attributes.getProperty("targetModule");
        String targetPackage = attributes.getProperty("targetPackage");
        String template = attributes.getProperty("template");
        String overwrite = attributes.getProperty("overwrite");
        daoGeneratorConfig.setTargetModule(targetModule);
        daoGeneratorConfig.setTargetPackage(targetPackage);
        daoGeneratorConfig.setTemplate(template);
        daoGeneratorConfig.setOverwrite(StrUtil.isEmpty(overwrite) ? false : Boolean.parseBoolean(overwrite));
        context.setDaoGeneratorConfig(daoGeneratorConfig);
    }

    private void parseMapperXmlGenerator(ContextConfig context, Node node) {
        MapperXmlGeneratorConfig mapperXmlGeneratorConfig = new MapperXmlGeneratorConfig();
        Properties attributes = this.parseAttributes(node);
        String targetModule = attributes.getProperty("targetModule");
        String targetDirectory = attributes.getProperty("targetDirectory");
        String template = attributes.getProperty("template");
        String overwrite = attributes.getProperty("overwrite");
        mapperXmlGeneratorConfig.setTargetModule(targetModule);
        mapperXmlGeneratorConfig.setTargetDirectory(targetDirectory);
        mapperXmlGeneratorConfig.setTemplate(template);
        mapperXmlGeneratorConfig.setOverwrite(StrUtil.isEmpty(overwrite) ? false : Boolean.parseBoolean(overwrite));
        context.setMapperXmlGeneratorConfig(mapperXmlGeneratorConfig);
    }

    private void parseDtoGenerator(ContextConfig context, Node node) {
        DtoGeneratorConfig dtoGeneratorConfig = new DtoGeneratorConfig();
        Properties attributes = this.parseAttributes(node);
        String targetModule = attributes.getProperty("targetModule");
        String targetPackage = attributes.getProperty("targetPackage");
        String template = attributes.getProperty("template");
        String overwrite = attributes.getProperty("overwrite");
        dtoGeneratorConfig.setTargetModule(targetModule);
        dtoGeneratorConfig.setTargetPackage(targetPackage);
        dtoGeneratorConfig.setTemplate(template);
        dtoGeneratorConfig.setOverwrite(StrUtil.isEmpty(overwrite) ? false : Boolean.parseBoolean(overwrite));
        context.setDtoGeneratorConfig(dtoGeneratorConfig);
    }

    private void parseVoGenerator(ContextConfig context, Node node) {
        VoGeneratorConfig voGeneratorConfig = new VoGeneratorConfig();
        Properties attributes = this.parseAttributes(node);
        String targetModule = attributes.getProperty("targetModule");
        String targetPackage = attributes.getProperty("targetPackage");
        String template = attributes.getProperty("template");
        String overwrite = attributes.getProperty("overwrite");
        voGeneratorConfig.setTargetModule(targetModule);
        voGeneratorConfig.setTargetPackage(targetPackage);
        voGeneratorConfig.setTemplate(template);
        voGeneratorConfig.setOverwrite(StrUtil.isEmpty(overwrite) ? false : Boolean.parseBoolean(overwrite));
        context.setVoGeneratorConfig(voGeneratorConfig);
    }

    private void parseTable(ContextConfig context, Node node) {
        TableConfig tableConfig = new TableConfig();
        Properties attributes = this.parseAttributes(node);
        String entityName = attributes.getProperty("entityName");
        String tableName = attributes.getProperty("tableName");
        tableConfig.setEntityName(entityName);
        tableConfig.setTableName(tableName);
        if (CollectionUtil.isEmpty(context.getTableConfigList())) {
            context.setTableConfigList(new LinkedList<>());
        }
        context.getTableConfigList().add(tableConfig);
    }


    protected Properties parseAttributes(Node node) {
        Properties attributes = new Properties();
        NamedNodeMap nnm = node.getAttributes();

        for (int i = 0; i < nnm.getLength(); ++i) {
            Node attribute = nnm.item(i);
            String value = attribute.getNodeValue();
            attributes.put(attribute.getNodeName(), value);
        }

        return attributes;
    }

    private void parseExtraPropertyGenerator(ContextConfig context, Node node) {
        ExtraPropertiesConfig extraPropertiesConfig = new ExtraPropertiesConfig();
        Map<String, String> properties = new HashMap<String, String>();
        NodeList childNode = node.getChildNodes();
        for (int i = 0; i < childNode.getLength(); i++) {
            Node child = childNode.item(i);
            if (child.getNodeType() == 1) {
                Properties attributes = parseAttributes(child);
                String name = attributes.getProperty("name");
                String value = attributes.getProperty("value");
                properties.put(name, value);
            }
        }
        extraPropertiesConfig.setExtraProperties(properties);
        context.setExtraPropertiesConfig(extraPropertiesConfig);
    }


}
